/* Includes ------------------------------------------------------------------*/
#include "ntp.h"
#include <stdarg.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>

#include "nril.h"
#include "convert.h"
/* typedef -------------------------------------------------------------------*/
struct NtpMessage
{
	uint8_t 	lvm;
	uint8_t 	stratum;
	uint8_t 	poll;
	uint8_t 	precision;
	
	uint32_t 	rootDelay;
	uint32_t	rootDispersion;
	char		referenceId[4];
	
	uint32_t 	referenceTimestampInt;
	uint32_t	referenceTimestampDecimal;
	
	/* T0, the time of client transmit */
	uint32_t 	originateTimestampInt;			
	uint32_t	originateTimestampDecimal;
	
	/* T1, the time of server receive request messsage */
	uint32_t	receiveTimestampInt;
	uint32_t	receiveTimestampDecimal;
	
	/* T2, the time of server transmit reponse message */
	uint32_t	transmitTimestampInt;
	uint32_t	transmitTimestampDecimal;
};
/* define --------------------------------------------------------------------*/
#define NTP_LI_NONE			(0<<6)
#define NTP_LI_61			(1<<6)
#define NTP_LI_59			(2<<6)
#define NTP_LI_WARN			(3<<6)
	
#define NTP_VERSION_1		(1<<3)
#define NTP_VERSION_2		(2<<3)
#define NTP_VERSION_3		(3<<3)

#define NTP_MODE_ATV_SMT	(1<<0)
#define NTP_MODE_PSV_SMT	(2<<0)
#define NTP_MODE_CLIENT		(3<<0)
#define NTP_MODE_SERVER		(4<<0)
#define NTP_MODE_BOARDCAST	(5<<0)
#define NTP_MODE_NTP_CTRL	(6<<0)
/* macro ---------------------------------------------------------------------*/
/* variables -----------------------------------------------------------------*/
/* function prototypes -------------------------------------------------------*/
extern uint32_t NETick_Get(void);
extern uint32_t NETimer_Get(void);
extern uint32_t NETimestamp_GetInteger(void);
extern double NETimestamp_Get(void);
extern double NETimestamp_GetDecimal(void);
extern double NETimestamp_Set(double n);

extern void NEDelay_Ms(int n);
/* functions -----------------------------------------------------------------*/

int Ntp(char *server, int waitTime)
{
	struct sockaddr_at addrNtp;
	int result = -1, socketNtp = -1;
	uint32_t *temp32;
	struct NtpMessage ntpMsg = {0};
	double t0,t1,t2,t3,d;
	
	addrNtp.sat_addr = server;
	addrNtp.sat_port = 123;
	
	/* step1, create a udp socket */
	socketNtp = socket(0, SOCK_DGRAM, 0);
	if(socketNtp < 0)
	{
		result = -1;
		goto __end;
	}
	
	/* step2, fill T1 into ntp message*/
	ntpMsg.lvm = NTP_LI_NONE | NTP_VERSION_3 | NTP_MODE_CLIENT;
	ntpMsg.stratum = 0x01;
	ntpMsg.poll = 0x04;
	ntpMsg.precision = 0xEC;
	ntpMsg.referenceTimestampInt = NETimestamp_GetInteger();
	ntpMsg.referenceTimestampDecimal = FloatDecimalToBin32((double)NETimestamp_GetDecimal() / 1000);
	ntpMsg.originateTimestampInt = NETimestamp_GetInteger();
	ntpMsg.originateTimestampDecimal = FloatDecimalToBin32((double)NETimestamp_GetDecimal() / 1000);
	EndianConvet(&ntpMsg.originateTimestampInt, 4);
	EndianConvet(&ntpMsg.originateTimestampDecimal, 4);
	
__resend:	
	
	/* step3, send message to ntp server*/
	sendto(socketNtp, &ntpMsg, sizeof(struct NtpMessage), 0, (const struct sockaddr *)&addrNtp, sizeof(struct sockaddr_at));
	t0 = NETimestamp_Get();
	
	
	for(int i=0; i<(waitTime/100); i++)
	{
		NEDelay_Ms(100);
		/* step4, send message to ntp server*/
		if(recvfrom(socketNtp, &ntpMsg, sizeof(struct NtpMessage), 0, (struct sockaddr *)&addrNtp, (uint32_t *)&result) > 0)
		{	
			temp32 = &(ntpMsg.referenceTimestampInt);
			for(int j=0; j<8; j++)
			{	
				EndianConvet(temp32, 4);	
				temp32 ++;
			}
			
			if(ntpMsg.transmitTimestampInt == 0)
			{	
				result = -1;
				goto __resend;	
			}
			
			t1 = (double)ntpMsg.receiveTimestampInt + Bin32ToDecimal(ntpMsg.receiveTimestampDecimal);
			t2 = (double)ntpMsg.transmitTimestampInt + Bin32ToDecimal(ntpMsg.transmitTimestampDecimal);
			t3 = NETimestamp_Get();
			
			d = (t3 - t0) - (t2 - t1);
			
			NETimestamp_Set(t2 + d - 2208988800);		// unix timestamp is from 1900. need reduce the second count of 70 years
			
			result = 0;
			break;
		}
	}
	
	disconnect(socketNtp);
	
__end:
	if(socket >= 0)
	{	closesocket(socketNtp);	}
	return result;
}
